package com.mzj.saas.mservice.bill.service;

import com.mzj.saas.commons.Constant;
import com.mzj.saas.commons.DateUtils;
import com.mzj.saas.commons.KeyWorker;
import com.mzj.saas.commons.ResultBean;
import com.mzj.saas.commons.enums.*;
import com.mzj.saas.commons.util.ConvertUtils;
import com.mzj.saas.commons.util.DataUtils;
import com.mzj.saas.commons.vo.redis.TokenRedisVo;
import com.mzj.saas.config.pay.PayConfig;
import com.mzj.saas.mservice.backlog.entity.BacklogItem;
import com.mzj.saas.mservice.backlog.entity.BacklogItemContractBill;
import com.mzj.saas.mservice.backlog.entity.BacklogItemRecordSubmit;
import com.mzj.saas.mservice.backlog.repository.BacklogItemContractBillRepository;
import com.mzj.saas.mservice.backlog.repository.BacklogItemRecordSubmitRepository;
import com.mzj.saas.mservice.backlog.repository.BacklogItemRepository;
import com.mzj.saas.mservice.bill.entity.*;
import com.mzj.saas.mservice.bill.mapper.ExpenseBillRowMapper;
import com.mzj.saas.mservice.bill.repository.*;
import com.mzj.saas.mservice.bill.vo.*;
import com.mzj.saas.mservice.community.entity.Community;
import com.mzj.saas.mservice.community.entity.CommunityBuild;
import com.mzj.saas.mservice.community.entity.CommunityBuildUnit;
import com.mzj.saas.mservice.community.repository.CommunityBuildRepository;
import com.mzj.saas.mservice.community.repository.CommunityBuildUnitRepository;
import com.mzj.saas.mservice.community.repository.CommunityRepository;
import com.mzj.saas.mservice.contract.entity.Contract;
import com.mzj.saas.mservice.contract.repository.ContractRepository;
import com.mzj.saas.mservice.eligible.entity.DepartmentHierarchy;
import com.mzj.saas.mservice.eligible.service.DepartmentHierarchyService;
import com.mzj.saas.mservice.house.entity.House;
import com.mzj.saas.mservice.house.repository.HouseRepository;
import com.mzj.saas.mservice.property.entity.EstateExpenseBill;
import com.mzj.saas.mservice.property.repository.EstateExpenseBillRepository;
import com.mzj.saas.mservice.propertybill.service.WithDrawService;
import com.mzj.saas.mservice.sys.entity.AppUser;
import com.mzj.saas.mservice.sys.entity.User;
import com.mzj.saas.mservice.sys.entity.WechatAccount;
import com.mzj.saas.mservice.sys.repository.AppUserRepository;
import com.mzj.saas.mservice.sys.repository.UserRepository;
import com.mzj.saas.mservice.sys.repository.WechatAccountRepository;
import com.mzj.saas.mservice.sys.utils.SqlUtils;
import com.mzj.saas.redis.RedisService;
import com.mzj.saas.sdk.pay.alipay.AliPayRequest;
import com.mzj.saas.sdk.pay.vo.AliPay;
import com.mzj.saas.sdk.pay.vo.WxPay;
import com.mzj.saas.sdk.pay.wxpay.WxPayRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class BillAppService {
    private static final Logger log = LoggerFactory.getLogger(BillAppService.class);

    private static final String BILL_COUNT = "SELECT COUNT(1) ";
    private static final String BILL_COLUM = "SELECT DISTINCT e.id,e.`house_id` AS houseId,e.`community_id` AS communityId,e.`building_num` AS buildingNum,\n"
            + "e.`unit`,e.`room_number` AS roomNumber,e.`match_user_id` AS matchUserId,e.`charge_type` AS chargeType,\n"
            + "e.`measure_unit` AS measureUnit,e.`cost`,e.`start_time` AS startTime,e.`end_time` AS endTime,e.`payment_status` AS paymentStatus,\n"
            + "d.`water_start_num` AS waterStartNum,d.`water_end_num` AS waterEndNum,d.`water_usage` AS waterUsage,d.`water_unit_price` AS waterUnitPrice,\n"
            + "d.`electric_start_num` AS electricStartNum,d.`electric_end_num` AS electricEndNum,d.`electric_usage` AS electricUsage,d.`electric_unit_price` AS electricUnitPrice,\n"
            + "d.`gas_start_num` AS gasStartNum,d.`gas_end_num` AS gasEndNum,d.`gas_usage` AS gasUsage,d.`gas_unit_price` AS gasUnitPrice,\n"
            + "d.`heating_bill` AS heatingBill,d.`property_fee` AS propertyFee,d.`tv_fee` AS tvFee,d.`broad_band_charges` AS broadBandCharges,e.should_pay_time as shouldPayTime";
    private static final String BILL_FROM_MONITOR = " FROM `saas_expense_bill` e INNER JOIN `saas_house_wait_payment_detail` d ON e.id = d.`expense_bill_id`";
    private static final String BILL_ORDER = " ORDER BY e.create_time DESC";
    private static final String SQL_PAGE = " LIMIT ? OFFSET ?";

    private static final String ORDER_COUNT = "SELECT COUNT(1) ";
    private static final String ORDER_COLUM = "SELECT DISTINCT r.bill_id AS billId,r.bill_amount AS billAmount,r.business_type AS businessType,o.pay_time AS payTime,o.pay_result AS payResult,\n" +
            "b1.community_id as communityId1,b2.community_id AS communityId2,b1.building_num as buildingNum1,b2.building_num AS buildingNum2,\n" +
            "b1.unit as unit1,b2.unit AS unit2,b1.room_number as roomNumber1,b2.room_number as roomNumber2";
    private static final String ORDER_FROM_MONITOR = " FROM saas_house_payment_order_relation r INNER JOIN saas_house_payment_order o ON r.order_number = o.order_number "
    		+ "LEFT JOIN saas_rent_bill b1 ON r.bill_id = b1.`id` "
            + "LEFT JOIN saas_expense_bill b2 ON r.bill_id = b2.`id` ";
    private static final String ORDER_ORDER = " ORDER BY o.pay_time DESC";


    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private RedisService redisService;
    @Autowired
    private PayConfig payConfig;
    @Autowired
    private CommunityRepository communityRepository;
    @Autowired
    private CommunityBuildRepository communityBuildRepository;
    @Autowired
    private CommunityBuildUnitRepository communityBuildUnitRepository;
    @Autowired
    private AppUserRepository appUserRepository;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private RentBillRepository rentBillRepository;
    @Autowired
    private ExpenseBillRepository expenseBillRepository;
    @Autowired
    private EstateExpenseBillRepository estateExpenseBillRepository;
    @Autowired
    private WechatAccountRepository wechatAccountRepository;
    @Autowired
    private DepartmentHierarchyService departmentHierarchyService;
    @Autowired
    private BacklogItemContractBillRepository backlogItemContractBillRepository;
    @Autowired
    private HousePaymentOrderRelationRepository housePaymentOrderRelationRepository;
    @Autowired
    private HouseRepository houseRepository;
    @Autowired
    private ContractRepository contractRepository;
    @Autowired
    private BacklogItemRepository backlogItemRepository;
    @Autowired
    private BacklogItemRecordSubmitRepository backlogItemRecordSubmitRepository;
    @Autowired
    private HousePaymentOrderRepository housePaymentOrderRepository;
    @Autowired
    private RentBillRelationRepository rentBillRelationRepository;
    @Autowired
    private WithDrawService withDrawService;

    public ResultBean<BillApiData<RentBillListVO>> rentBillList(String accessToken, String residenceCommunityId,
                                                                Long buildingNum, Long unit, String roomNumber, Integer pageNumber, Integer pageSize,Long id) {
        try {
            log.info("BillAppService rentBillList,residenceCommunityId={},buildingNum={},unit={}.roomNumber={},id={}",residenceCommunityId,buildingNum,
                    unit,roomNumber,id);
            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }
            String userId = tokenRedisVo.getId();
            String sql="select t1.houseId from  (\n" +
                    "SELECT a.house_id as houseId FROM  saas_house_match_user a where a.status=1 and a.match_user_id=?\n" +
                    "UNION \n" +
                    "SELECT c.house_id FROM  saas_app_user a\n" +
                    "LEFT JOIN saas_house_eligible_apply_cohabit b on b.mobile= a.phone and b.status in (1)\n" +
                    "LEFT JOIN saas_house_match_user c on c.apply_id= b.apply_id and c.status=1\n" +
                    "where a.id=?\n" +
                    ")t1";
            List<Object> args = new ArrayList<>();
            args.add(userId);
            args.add(userId);
            List<RentBillListVO> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(RentBillListVO.class), args.toArray());
            List<String> houseIdList = list.stream().map(x -> x.getHouseId()).collect(Collectors.toList());

            BillApiData<RentBillListVO> apiData = new BillApiData<>();
            if (null == houseIdList || houseIdList.size()<=0){
                return ResultBean.successfulResult(apiData);
            }

            List<RentBillListVO> data = new ArrayList<>();
            long total = 0;
            if (id != null ){
                String rentBillSql="select * from  saas_rent_bill where  1=1 ";
                StringBuilder where  = new StringBuilder();
                ArrayList<Object> arg = new ArrayList<>();

                where.append(" and payment_status=0 and audit_status=1 ");

                where.append(" and house_id in(       select t1.houseId from  (SELECT a.house_id as houseId FROM  saas_house_match_user a where a.status=1 and a.match_user_id=?\n" +
                        "UNION\n" +
                        "SELECT c.house_id FROM  saas_app_user a\n" +
                        "LEFT JOIN saas_house_eligible_apply_cohabit b on b.mobile= a.phone and b.status in (1)\n" +
                        "LEFT JOIN saas_house_match_user c on c.apply_id= b.apply_id and c.status=1\n" +
                        "where a.id=? )t1) ");
                arg.add(userId);
                arg.add(userId);

                if (StringUtils.isNotBlank(residenceCommunityId)) {
                    where.append(" and community_id = ? ");
                    arg.add(residenceCommunityId);
                }
                if (buildingNum != null) {
                    where.append(" and building_num = ? ");
                    arg.add(buildingNum);
                }
                if (unit != null) {
                    where.append(" and unit = ? ");
                    arg.add(unit);
                }
                if (StringUtils.isNotBlank(roomNumber)) {
                    where.append(" and room_number = ? ");
                    arg.add(roomNumber);

                }

                List<RentBill> query = jdbcTemplate.query(rentBillSql + where, new BeanPropertyRowMapper<>(RentBill.class), arg.toArray());
                total = query.size();
                if (total == 0) {
                    return ResultBean.successfulResult(apiData);
                }

                where.append(" ORDER BY FIELD(id, "+id+") DESC ,create_time DESC ");

                arg.add(pageSize);
                arg.add(pageNumber * pageSize);
//
                List<RentBill> rentBillList = jdbcTemplate.query(rentBillSql + where + SQL_PAGE, new BeanPropertyRowMapper<>(RentBill.class), arg.toArray());

                for (RentBill rentBill : rentBillList) {
                    data.add(convert(rentBill));

                }

            }else {
                Page<RentBillListVO> page = rentBillRepository.findAll((root, query, criteriaBuilder) -> {
                    Predicate predicate = criteriaBuilder.and();
                    //predicate = criteriaBuilder.and(predicate, criteriaBuilder.in(root.get("matchUserId")).value(houseIdList));
                    predicate = criteriaBuilder.and(predicate, criteriaBuilder.in(root.get("houseId")).value(houseIdList));
                    predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("paymentStatus"), 0));
                    predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("auditStatus"), 1));
                    if (StringUtils.isNotBlank(residenceCommunityId)) {
                        predicate = criteriaBuilder.and(predicate,
                                criteriaBuilder.equal(root.get("communityId"), residenceCommunityId));
                    }
                    if (buildingNum != null) {
                        predicate = criteriaBuilder.and(predicate,
                                criteriaBuilder.equal(root.get("buildingNum"), buildingNum));
                    }
                    if (unit != null) {
                        predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(root.get("unit"), unit));
                    }
                    if (StringUtils.isNotBlank(roomNumber)) {
                        predicate = criteriaBuilder.and(predicate,
                                criteriaBuilder.equal(root.get("roomNumber"), roomNumber));
                    }
                    return predicate;

                }, PageRequest.of(pageNumber, pageSize, Sort.Direction.DESC, "createTime")).map(this::convert);

                 total = page.getTotalElements();
                 data = page.getContent();

                if (total == 0) {
                    return ResultBean.successfulResult(apiData);
                }

            }


            // 查询小区名称
            List<String> residenceCommunityIds = data.stream().filter(vo -> vo.getCommunityId() != null)
                    .map(RentBillListVO::getCommunityId).distinct().collect(Collectors.toList());
            if (!residenceCommunityIds.isEmpty()) {
                Map<String, String> map = communityRepository.findByIdIn(residenceCommunityIds).stream()
                        .collect(Collectors.toMap(Community::getId, Community::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setCommunityName(map.get(vo.getCommunityId())));
                }
            }
            // 查询楼栋名称
            List<Long> buildingIds = data.stream().filter(vo -> vo.getBuildingNum() != null)
                    .map(RentBillListVO::getBuildingNum).distinct().collect(Collectors.toList());
            if (!buildingIds.isEmpty()) {
                Map<Long, String> map = communityBuildRepository.findByIdIn(buildingIds).stream()
                        .collect(Collectors.toMap(CommunityBuild::getId, CommunityBuild::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setBuildingName(map.get(vo.getBuildingNum())));
                }
            }
            // 查询单元名称
            List<Long> unitIds = data.stream().filter(vo -> vo.getUnit() != null).map(RentBillListVO::getUnit)
                    .distinct().collect(Collectors.toList());
            if (!unitIds.isEmpty()) {
                Map<Long, String> map = communityBuildUnitRepository.findByIdIn(unitIds).stream()
                        .collect(Collectors.toMap(CommunityBuildUnit::getId, CommunityBuildUnit::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setUnitName(map.get(vo.getUnit())));
                }
            }
            // 查询承租人名称
            List<String> userIds = data.stream().filter(vo -> vo.getMatchUserId() != null)
                    .map(RentBillListVO::getMatchUserId).distinct().collect(Collectors.toList());
            if (!userIds.isEmpty()) {
                List<AppUser> appUsers = appUserRepository.findByIdOrEnterpriseIn(userIds, userIds);
                for (AppUser appUser : appUsers) {
                    for (RentBillListVO vo : data) {
                        if (vo.getMatchUserId().equals(appUser.getId()) || vo.getMatchUserId().equals(appUser.getEnterpriseUserId())) {
                            vo.setMatchUserName(appUser.getName());
                        }
                    }
                }
            }

            // 手续费比例
            String platStr = redisService.getValue("platform.commission");
            BigDecimal commission = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));

            apiData.setTotal(total);
            apiData.setData(data);
            apiData.setCommission(commission);
            return ResultBean.successfulResult(apiData);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    private RentBillListVO convert(RentBill entity) {
        RentBillListVO vo = new RentBillListVO();
        vo.setId(entity.getId());
        vo.setHouseId(entity.getHouseId());
        vo.setCommunityId(entity.getCommunityId());
        vo.setBuildingNum(entity.getBuildingNum());
        vo.setUnit(entity.getUnit());
        vo.setRoomNumber(entity.getRoomNumber());
        vo.setMatchUserId(entity.getMatchUserId());
        vo.setChargeType(entity.getChargeType());
        vo.setMeasureUnit(entity.getMeasureUnit());
        vo.setCost(ConvertUtils.fenConvertYuan(entity.getCost()));// 分转元
        vo.setShouldPayTime(DateUtils.format(entity.getShouldPayTime(), DateUtils.FORMAT_DATE_8));
        vo.setStartTime(DateUtils.format(entity.getStartTime(), DateUtils.FORMAT_DATE_8));
        vo.setEndTime(DateUtils.format(entity.getEndTime(), DateUtils.FORMAT_DATE_8));
        vo.setPaymentStatus(entity.getPaymentStatus());
        return vo;
    }

    public ResultBean<RentBillDetailVO> rentBillDetail(Long id) {
        try {
            if (id == null) {
                return ResultBean.failedResultOfParamWithMsg("账单id为空");
            }

            Optional<RentBill> optional = rentBillRepository.findById(id);
            if (!optional.isPresent()) {
                return ResultBean.failedResultWithMsg("账单不存在");
            }
            RentBill rentBill = optional.get();
            RentBillDetailVO vo = new RentBillDetailVO();

            Optional<Community> communityOptional = communityRepository.findById(rentBill.getCommunityId());
            if (!communityOptional.isPresent()) {
                return ResultBean.failedResultWithMsg("小区不存在");
            }
            Community community = communityOptional.get();
            vo.setCommunityName(community.getName());

            Optional<CommunityBuild> buildOptional = communityBuildRepository.findById(rentBill.getBuildingNum());
            if (!buildOptional.isPresent()) {
                return ResultBean.failedResultWithMsg("楼栋不存在");
            }
            CommunityBuild build = buildOptional.get();
            vo.setBuildingName(build.getName());

            Optional<CommunityBuildUnit> unitOptional = communityBuildUnitRepository.findById(rentBill.getUnit());
            if (!unitOptional.isPresent()) {
                return ResultBean.failedResultWithMsg("单元不存在");
            }
            CommunityBuildUnit unit = unitOptional.get();
            vo.setUnitName(unit.getName());

            vo.setRoomNumber(rentBill.getRoomNumber());

            AppUser user = appUserRepository.findByIdOrEnterpriseUserId(rentBill.getMatchUserId(), rentBill.getMatchUserId());
            if (user == null) {
                return ResultBean.failedResultWithMsg("用户不存在");
            }
            vo.setMatchUserName(user.getName());
            vo.setPhone(user.getPhone());

            vo.setChargeType(rentBill.getChargeType());
            Integer cost = rentBill.getCost();
            vo.setCost(ConvertUtils.fenConvertYuan(cost));
            vo.setShouldPayTime(
                    DateUtils.format(DateUtils.getYesterday(rentBill.getStartTime()), DateUtils.FORMAT_DATE_8));
            vo.setStartTime(DateUtils.format(rentBill.getStartTime(), DateUtils.FORMAT_DATE_8));
            vo.setEndTime(DateUtils.format(rentBill.getEndTime(), DateUtils.FORMAT_DATE_8));

            // 手续费比例
            String platStr = redisService.getValue("platform.commission");
            BigDecimal commission = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));

            BigDecimal commissionCost = ConvertUtils.fen2yuan(cost).multiply(commission);// 手续费
            vo.setCommissionCost(commissionCost.setScale(2).toString());

            BigDecimal amount = ConvertUtils.fen2yuan(cost).add(commissionCost);// 合计金额
            vo.setAmount(amount.setScale(2).toString());

            return ResultBean.successfulResult(vo);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    public ResultBean<BillApiData<ExpenseBillListVO>> expenseBillList(String accessToken, String residenceCommunityId,
                                                                      Long buildingNum, Long unit, String roomNumber, Integer chargeType, Integer pageNumber, Integer pageSize,Long id) {
        ResultBean<BillApiData<ExpenseBillListVO>> resultBean = new ResultBean<>();
        resultBean.setCode("10000");
        resultBean.setMsg("10000");
        BillApiData<ExpenseBillListVO> apiData = new BillApiData<>();
        resultBean.setResultData(apiData);
        try {
            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }
            String userId = tokenRedisVo.getId();

            StringBuilder where = new StringBuilder();
            List<Object> args = new ArrayList<>();
            where.append(" where 1 = 1");
            where.append(" and e.`house_id` in (select t1.houseId from  (\n" +
                    "SELECT a.house_id AS houseId FROM  saas_house_match_user a LEFT JOIN saas_app_user ap ON a.match_user_id = ap.id WHERE a.status=1 AND (a.match_user_id = ? OR ap.phone = ?)\n" +
                    "UNION \n" +
                    "SELECT c.house_id FROM  saas_app_user a\n" +
                    "LEFT JOIN saas_house_eligible_apply_cohabit b on b.mobile= a.phone and b.status in (1)\n" +
                    "LEFT JOIN saas_house_match_user c on c.apply_id= b.apply_id and c.status=1\n" +
                    "where a.id=? OR a.phone = ? \n" +
                    ")t1)");
            args.add(userId);
            // 通过登录用户手机号码也可以查询到账单
            Integer loginType = tokenRedisVo.getLoginType();
            String phone = null;
            if (loginType == 1) {
                User user = userRepository.findById(userId);
                phone = user.getMobile();
            } else {
                AppUser appUser = appUserRepository.findByIdOrEnterpriseUserId(userId, userId);
                phone = appUser.getPhone();
            }
            args.add(phone);
            args.add(userId);
            args.add(phone);
            
            where.append(" and e.audit_status = 1 and e.`payment_status` = 0");

            if (!StringUtils.isBlank(residenceCommunityId)) {
                where.append(" and e.`community_id` = ?");
                args.add(residenceCommunityId);
            }
            if (buildingNum != null) {
                where.append(" and e.`building_num` = ?");
                args.add(buildingNum);
            }
            if (unit != null) {
                where.append(" and e.`unit` = ?");
                args.add(unit);
            }
            if (!StringUtils.isBlank(roomNumber)) {
                where.append(" and e.`room_number` = ?");
                args.add(roomNumber);
            }
            if (chargeType != null) {
                where.append(" and e.`charge_type` = ?");
                args.add(chargeType);
            }
            
            // 总数
            Long total = jdbcTemplate.queryForObject(BILL_COUNT + BILL_FROM_MONITOR + where, args.toArray(),
                    Long.class);
            if (total == 0) {
                return resultBean;
            }
            String billOrder= BILL_ORDER;
            if (id != null){
                billOrder= " ORDER BY FIELD(e.id, ?) DESC , e.create_time DESC ";
                args.add(id);
            }

            args.add(pageSize);
            args.add(pageNumber * pageSize);
            List<ExpenseBillListVO> data = jdbcTemplate.query(
                    BILL_COLUM + BILL_FROM_MONITOR + where + billOrder + SQL_PAGE, new ExpenseBillRowMapper(),
                    args.toArray());

            // 查询承租人名称
            List<String> ids = data.stream().filter(vo -> vo.getMatchUserId() != null).map(ExpenseBillListVO::getMatchUserId).distinct().collect(Collectors.toList());
            if (!ids.isEmpty()) {
                List<AppUser> appUsers = appUserRepository.findByIdOrEnterpriseIn(ids, ids);
                for (AppUser appUser : appUsers) {
                    for (ExpenseBillListVO vo : data) {
                        if (vo.getMatchUserId().equals(appUser.getId()) || vo.getMatchUserId().equals(appUser.getEnterpriseUserId())) {
                            vo.setMatchUserName(appUser.getName());
                        }
                    }
                }
            }
            // 查询小区名称
            List<String> residenceCommunityIds = data.stream().filter(vo -> vo.getCommunityId() != null)
                    .map(ExpenseBillListVO::getCommunityId).distinct().collect(Collectors.toList());
            if (!residenceCommunityIds.isEmpty()) {
                Map<String, String> map = communityRepository.findByIdIn(residenceCommunityIds).stream()
                        .collect(Collectors.toMap(Community::getId, Community::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setCommunityName(map.get(vo.getCommunityId())));
                }
            }
            // 查询楼栋名称
            List<Long> buildingIds = data.stream().filter(vo -> vo.getBuildingNum() != null)
                    .map(ExpenseBillListVO::getBuildingNum).distinct().collect(Collectors.toList());
            if (!buildingIds.isEmpty()) {
                Map<Long, String> map = communityBuildRepository.findByIdIn(buildingIds).stream()
                        .collect(Collectors.toMap(CommunityBuild::getId, CommunityBuild::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setBuildingName(map.get(vo.getBuildingNum())));
                }
            }
            // 查询单元名称
            List<Long> unitIds = data.stream().filter(vo -> vo.getUnit() != null).map(ExpenseBillListVO::getUnit)
                    .distinct().collect(Collectors.toList());
            if (!unitIds.isEmpty()) {
                Map<Long, String> map = communityBuildUnitRepository.findByIdIn(unitIds).stream()
                        .collect(Collectors.toMap(CommunityBuildUnit::getId, CommunityBuildUnit::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setUnitName(map.get(vo.getUnit())));
                }
            }

            // 手续费比例
            String platStr = redisService.getValue("platform.commission");
            BigDecimal commission = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));

            apiData.setData(data);
            apiData.setTotal(total);
            apiData.setCommission(commission);
            resultBean.setResultData(apiData);

        } catch (Exception e) {
            log.error("BillAppService expenseBillList Exception = {}" + e.getMessage(), e);
            resultBean.setCode("10001");
            resultBean.setMsg("10001");
        }
        resultBean.setResultData(apiData);
        return resultBean;
    }

    /**
     * 支付记录列表
     *
     * @param accessToken
     * @param businessType
     * @param pageNumber
     * @param pageSize
     * @return
     */
    public ResultBean<PaymentOrderApiData<PaymentOrderListVO>> paymentOrderList(String accessToken,
                                                                                Integer businessType, Integer pageNumber, Integer pageSize) {
        ResultBean<PaymentOrderApiData<PaymentOrderListVO>> resultBean = new ResultBean<>();
        resultBean.setCode("10000");
        resultBean.setMsg("10000");
        PaymentOrderApiData<PaymentOrderListVO> apiData = new PaymentOrderApiData<>();
        resultBean.setResultData(apiData);
        try {
            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }
            String userId = tokenRedisVo.getId();
            Long enterpriseId = tokenRedisVo.getEnterpriseId();

            StringBuilder where = new StringBuilder();
            List<Object> args = new ArrayList<>();
            where.append(" where 1 = 1");
            where.append(" and o.`pay_user_id` = ? ");
            args.add(userId);

            if (businessType != null) {
                where.append(" and r.`business_type` = ?");
                args.add(businessType);
            }

            // 总数
            Long total = jdbcTemplate.queryForObject(ORDER_COUNT + ORDER_FROM_MONITOR + where, args.toArray(),
                    Long.class);
            if (total == 0) {
                return resultBean;
            }

            Long totalAmount = jdbcTemplate.queryForObject(
                    "SELECT SUM(r.bill_amount) AS totalAmount" + ORDER_FROM_MONITOR + where, args.toArray(),
                    Long.class);

            args.add(pageSize);
            args.add(pageNumber * pageSize);
            List<PaymentOrderListVO> data = jdbcTemplate.query(ORDER_COLUM + ORDER_FROM_MONITOR + where + ORDER_ORDER + SQL_PAGE,
                    new BeanPropertyRowMapper<>(PaymentOrderListVO.class), args.toArray());

            for (PaymentOrderListVO vo : data) {
                vo.setBillAmount(ConvertUtils.fenConvertYuan(Long.parseLong(vo.getBillAmount())));
                vo.setPayTime(DateUtils.format(vo.getPayTime(), DateUtils.FORMAT_DATETIME_14, DateUtils.FORMAT_DATETIME_14));

                if (vo.getBusinessType() == 1 || vo.getBusinessType() == 9) {
                    vo.setCommunityId(vo.getCommunityId1());
                    vo.setBuildingNum(vo.getBuildingNum1());
                    vo.setUnit(vo.getUnit1());
                    vo.setRoomNumber(vo.getRoomNumber1());
                } else {
                    vo.setCommunityId(vo.getCommunityId2());
                    vo.setBuildingNum(vo.getBuildingNum2());
                    vo.setUnit(vo.getUnit2());
                    vo.setRoomNumber(vo.getRoomNumber2());
                }
            }

            // 查询小区名称
            List<String> residenceCommunityIds = data.stream().filter(vo -> vo.getCommunityId() != null)
                    .map(PaymentOrderListVO::getCommunityId).distinct().collect(Collectors.toList());
            if (!residenceCommunityIds.isEmpty()) {
                Map<String, String> map = communityRepository.findByIdIn(residenceCommunityIds).stream()
                        .collect(Collectors.toMap(Community::getId, Community::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setCommunityName(map.get(vo.getCommunityId())));
                }
            }
            // 查询楼栋名称
            List<Long> buildingIds = data.stream().filter(vo -> vo.getBuildingNum() != null)
                    .map(PaymentOrderListVO::getBuildingNum).distinct().collect(Collectors.toList());
            if (!buildingIds.isEmpty()) {
                Map<Long, String> map = communityBuildRepository.findByIdIn(buildingIds).stream()
                        .collect(Collectors.toMap(CommunityBuild::getId, CommunityBuild::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setBuildingName(map.get(vo.getBuildingNum())));
                }
            }
            // 查询单元名称
            List<Long> unitIds = data.stream().filter(vo -> vo.getUnit() != null).map(PaymentOrderListVO::getUnit)
                    .distinct().collect(Collectors.toList());
            if (!unitIds.isEmpty()) {
                Map<Long, String> map = communityBuildUnitRepository.findByIdIn(unitIds).stream()
                        .collect(Collectors.toMap(CommunityBuildUnit::getId, CommunityBuildUnit::getName));
                if (!map.isEmpty()) {
                    data.forEach(vo -> vo.setUnitName(map.get(vo.getUnit())));
                }
            }

            apiData.setData(data);
            apiData.setTotal(total);
            apiData.setTotalAmount(ConvertUtils.fenConvertYuan(totalAmount));
            resultBean.setResultData(apiData);
        } catch (Exception e) {
            log.error("BillAppService paymentOrderList Exception = {}" + e.getMessage(), e);
            resultBean.setCode("10001");
            resultBean.setMsg("10001");
        }
        resultBean.setResultData(apiData);
        return resultBean;
    }

    /**
     * 租金/押金app支付
     *
     * @param accessToken
     * @param vo
     * @return
     * @author shun
     * @date 2021年6月15日
     */
    public ResultBean<Map<String, Object>> payment(String accessToken, RentBillPaymentVo vo) {
        try {

            log.info("BillAppService payment vo={}", vo.toString());

            if (CollectionUtils.isEmpty(vo.getIds())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            if (vo.getPaymentMode() == null || vo.getPaymentType() == null || vo.getChargeType() == null) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            if (!PaymentTypeEnum.operate(vo.getPaymentType()) || !PaymentModeEnum.operate(vo.getPaymentMode())
                    || !ChargeTypeEnum.operate(vo.getChargeType())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }

            String userId = tokenRedisVo.getId();
            // 操作用户企业id
            Long enterpriseId = tokenRedisVo.getEnterpriseId();
            List<RentBill> rentBills = rentBillRepository.findByIdIn(vo.getIds());

            if (CollectionUtils.isEmpty(rentBills)) {
                log.warn("BillAppService payment 未找到租金费用明细 id={}", vo.getIds());
                return ResultBean.failedResultWithMsg("未找到租金费用明细,请重试");
            }

            if (rentBills.size() != vo.getIds().size()) {
                log.warn("BillAppService payment 查询数量不一致  ids={},bill={}", vo.getIds().size(), rentBills.size());
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            Integer cose = 0;
            for (RentBill rentBill : rentBills) {
                if (rentBill.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    log.warn("BillAppService payment 此租金费用已支付,无需重复支付 id={},houseId={}", rentBill.getId(),
                            rentBill.getHouseId());
                    return ResultBean.failedResultWithMsg("当前有租金费用已支付,无需重复支付");
                }
                cose += rentBill.getCost();
            }

            // 手续费比例
            String platStr = redisService.getValue("platform.commission");
            BigDecimal totalAmount = DataUtils.IntegerToBigdecimal(cose);
            Integer plat = 0;
            if (StringUtils.isNotBlank(platStr)) {
                BigDecimal platform = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));
                BigDecimal platAmount = totalAmount.multiply(platform).setScale(2, BigDecimal.ROUND_HALF_UP);
                totalAmount = totalAmount.add(platAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
                plat = DataUtils.doubleToInteger(platAmount.doubleValue());
            }

            Long orgId = rentBills.get(0).getOrgId();
            String outTradeNo = String.valueOf(KeyWorker.nextId());
            PaymentBillRedisVo redisVo = new PaymentBillRedisVo();
            redisVo.setOrgId(orgId);
            redisVo.setPaymentMode(vo.getPaymentMode());
            redisVo.setPaymentStatus(0);
            redisVo.setPlat(plat);
            redisVo.setUserId(userId);
            redisVo.setOutTradeNo(outTradeNo);
            redisVo.setIds(vo.getIds());
            redisVo.setTotalAmount(totalAmount);
            redisVo.setBusinessType(1);

            // 操作用户企业信息
            DepartmentHierarchy departmentHierarchy = departmentHierarchyService.getEnterpriseInfo(orgId);
            if (departmentHierarchy == null) {
                log.warn("BillAppService payment 未找到您当前所在机构/企业 userId={},enterpriseId={}", userId,
                        enterpriseId);
                return ResultBean.failedResultWithMsg("企业信息错误");
            }

            //0元支付
            if (DataUtils.doubleToInteger(totalAmount.doubleValue()) == 0) {
                return zeroPayment(redisVo);
            }

            // 支付方式
            if (vo.getPaymentType() == PaymentTypeEnum.WECHAT.getValue()) {
                WechatAccount account = null;
                // 根据企业设置, 获取收款方式(平台收款, 企业商户号收款)
                // 收款方式：0平台商户号收款 1企业商户号收款
                if (departmentHierarchy.getMerchantsType() == 0) {
                    // 0为平台收款商户号企业id
                    account = wechatAccountRepository.findByOrgIdAndType(0L,
                            PaymentTypeEnum.WECHAT.getValue());
                } else {
                    // 默认使用企业账户号收款
                    account = wechatAccountRepository.findByOrgIdAndType(orgId,
                            PaymentTypeEnum.WECHAT.getValue());
                }

                if (account == null) {
                    log.warn("BillAppService payment 未找到微信商户配置,请联系负责人orgId={}", orgId);
                    return ResultBean.failedResultWithMsg("未找到微信商户配置,请联系客服");
                }
                //应用ID或者分配的appId
                String appId = redisService.getValue(Constant.WX_APPID);
                String attac = outTradeNo;
                String spbillCreateIp = redisService.getValue(Constant.SPBILL_CREATE_IP);
                String body = "房屋管家-" + ChargeTypeEnum.getText(vo.getChargeType());
                cose = DataUtils.doubleToInteger(totalAmount.doubleValue());
                WxPay wxpay = new WxPay(appId, account.getMchId(), outTradeNo, attac, cose, body,
                        account.getMchApiKey(), null, payConfig.getWxNotifyUrl(), payConfig.getUnifiedApi(), spbillCreateIp);
                Map<String, Object> result = new WxPayRequest(wxpay).unifiedByApp();
                if (result == null) {
                    return ResultBean.failedResultWithMsg("微信支付异常,请联系客服");
                }
                redisVo.setMchApiKey(account.getMchApiKey());
                redisVo.setMerchantId(account.getId());
                redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
                redisService.putPaymentBill(redisVo);
                return ResultBean.successfulResult(result);
            }

            if (vo.getPaymentType() == PaymentTypeEnum.ALI.getValue()) {
                WechatAccount account = null;
                // 收款方式：0平台商户号收款 1企业商户号收款
                if (departmentHierarchy.getMerchantsType() == 0) {
                    // 0为平台收款商户号企业id
                    account = wechatAccountRepository.findByOrgIdAndType(0L,
                            PaymentTypeEnum.ALI.getValue());
                } else {
                    // 默认使用企业账户号收款
                    account = wechatAccountRepository.findByOrgIdAndType(orgId,
                            PaymentTypeEnum.ALI.getValue());
                }

                if (account == null) {
                    log.warn("BillAppService payment 未找到支付宝配置,请联系负责人 orgId={}", orgId);
                    return ResultBean.failedResultWithMsg("未找到支付宝配置,请联系负责人");
                }
                String subject = ChargeTypeEnum.getText(vo.getChargeType());
                String passbackParams = outTradeNo;

                AliPay alipay = new AliPay(account.getMchId(), account.getPrivateKey(), account.getPublicKey(),
                        outTradeNo, totalAmount.toString(), subject, "QUICK_MSECURITY_PAY", passbackParams,
                        payConfig.getAliNotifyUrl());

                Map<String, Object> result = new AliPayRequest(alipay).appPay();

                if (result == null) {
                    return ResultBean.failedResultWithMsg("支付宝支付异常,请联系客服");
                }
                redisVo.setPublicKey(account.getPublicKey());
                redisVo.setMerchantId(account.getId());
                redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
                redisService.putPaymentBill(redisVo);
                return ResultBean.successfulResult(result);
            }
            return ResultBean.failedResultOfParamWithMsg("参数错误");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    /**
     * 公租房-其它费用支付
     *
     * @param accessToken
     * @param vo
     * @return
     * @author shun
     * @date 2021年7月12日
     */
    public ResultBean<Map<String, Object>> otherPayment(String accessToken, RentBillPaymentVo vo) {
        try {
            log.info("BillAppService otherPayment vo={}", vo.toString());
            if (CollectionUtils.isEmpty(vo.getIds())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            if (vo.getPaymentMode() == null || vo.getPaymentType() == null || vo.getChargeType() == null) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            if (!PaymentTypeEnum.operate(vo.getPaymentType()) || !PaymentModeEnum.operate(vo.getPaymentMode())
                    || !ChargeTypeEnum.operate(vo.getChargeType())) {
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }

            String userId = tokenRedisVo.getId();
            // 操作用户企业id
            Long enterpriseId = tokenRedisVo.getEnterpriseId();
            List<EstateExpenseBill> expenseBills = estateExpenseBillRepository.findByIdIn(vo.getIds());

            if (CollectionUtils.isEmpty(expenseBills)) {
                log.warn("BillAppService otherPayment, 未找到其它费用明细, id={}", vo.getIds());
                return ResultBean.failedResultWithMsg("未找到账单费用明细, 请重试");
            }

            if (expenseBills.size() != vo.getIds().size()) {
                log.warn("BillAppService otherPayment, 查询数量不一致 , ids={}, bill={}", vo.getIds().size(), expenseBills.size());
                return ResultBean.failedResultOfParamWithMsg("参数错误");
            }

            Integer cose = 0;
            for (EstateExpenseBill expenseBill : expenseBills) {
                if (expenseBill.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    log.warn("BillAppService otherPayment, 此费用账单已支付, 无需重复支付, id={}, houseId={}", expenseBill.getId(),
                            expenseBill.getHouseId());
                    return ResultBean.failedResultWithMsg("存在已支付账单, 无需重复支付");
                }
                // 累计所有账单支付金额
                cose += expenseBill.getCost();
            }

            // 手续费比例
            String platStr = redisService.getValue("platform.commission");
            BigDecimal totalAmount = DataUtils.IntegerToBigdecimal(cose);
            Integer plat = 0;
            if (StringUtils.isNotBlank(platStr)) {
                BigDecimal platform = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));
                BigDecimal platAmount = totalAmount.multiply(platform).setScale(2, BigDecimal.ROUND_HALF_UP);
                totalAmount = totalAmount.add(platAmount).setScale(2, BigDecimal.ROUND_HALF_UP);
                plat = DataUtils.doubleToInteger(platAmount.doubleValue());
            }

            Long orgId = expenseBills.get(0).getOrgId();
            String outTradeNo = String.valueOf(KeyWorker.nextId());

            PaymentBillRedisVo redisVo = new PaymentBillRedisVo();
            redisVo.setOrgId(orgId);
            redisVo.setPaymentMode(vo.getPaymentMode());
            redisVo.setPaymentStatus(0);
            redisVo.setPlat(plat);
            redisVo.setUserId(userId);
            redisVo.setOutTradeNo(outTradeNo);
            redisVo.setIds(vo.getIds());
            redisVo.setTotalAmount(totalAmount);
            // 1.公租房租金/押金 2.报修 3.待办事项 4.物业房产租金/押金  5.生活缴费  6公租房其它费用  7物业房产其它费用
            redisVo.setBusinessType(6);

            // 操作用户企业信息
            DepartmentHierarchy departmentHierarchy = departmentHierarchyService.getEnterpriseInfo(orgId);
            if (departmentHierarchy == null) {
                log.warn("BillAppService otherPayment 未找到您当前所在机构/企业 userId={},enterpriseId={}", userId,
                        enterpriseId);
                return ResultBean.failedResultWithMsg("企业信息错误");
            }

            //0元支付
            if (DataUtils.doubleToInteger(totalAmount.doubleValue()) == 0) {
                return zeroOtherPayment(redisVo);
            }

            // 支付方式
            if (vo.getPaymentType() == PaymentTypeEnum.WECHAT.getValue()) {
                WechatAccount account = null;
                // 根据企业设置, 获取收款方式(平台收款, 企业商户号收款)
                // 收款方式：0平台商户号收款 1企业商户号收款
                if (departmentHierarchy.getMerchantsType() == 0) {
                    // 0为平台收款商户号企业id
                    account = wechatAccountRepository.findByOrgIdAndType(0L,
                            PaymentTypeEnum.WECHAT.getValue());
                } else {
                    // 默认使用企业账户号收款
                    account = wechatAccountRepository.findByOrgIdAndType(orgId,
                            PaymentTypeEnum.WECHAT.getValue());
                }

                if (account == null) {
                    log.warn("BillAppService otherPayment 未找到微信商户配置,请联系负责人orgId={}", orgId);
                    return ResultBean.failedResultWithMsg("未找到微信商户配置,请联系客服");
                }
                //应用ID或者分配的appId
                String appId = redisService.getValue(Constant.WX_APPID);
                String attac = outTradeNo;
                String spbillCreateIp = redisService.getValue(Constant.SPBILL_CREATE_IP);
                // 其它费用类型转换
                String chargeTypeName = "";
                if (vo.getChargeType() == 1) {
                    chargeTypeName = "水费";
                } else if (vo.getChargeType() == 2) {
                    chargeTypeName = "电费";
                } else if (vo.getChargeType() == 3) {
                    chargeTypeName = "物业费";
                } else if (vo.getChargeType() == 4) {
                    chargeTypeName = "宽带费";
                } else if (vo.getChargeType() == 5) {
                    chargeTypeName = "电视费";
                } else if (vo.getChargeType() == 6) {
                    chargeTypeName = "燃气费";
                } else if (vo.getChargeType() == 7) {
                    chargeTypeName = "暖气费";
                } else if (vo.getChargeType() == 12) {
                    chargeTypeName = "其它费用组合支付";
                }
                String body = "房屋管家-" + chargeTypeName;
                cose = DataUtils.doubleToInteger(totalAmount.doubleValue());
                WxPay wxpay = new WxPay(appId, account.getMchId(), outTradeNo, attac, cose, body,
                        account.getMchApiKey(), null, payConfig.getWxNotifyUrl(), payConfig.getUnifiedApi(), spbillCreateIp);
                Map<String, Object> result = new WxPayRequest(wxpay).unifiedByApp();
                if (result == null) {
                    return ResultBean.failedResultWithMsg("微信支付异常, 请联系客服");
                }
                redisVo.setMchApiKey(account.getMchApiKey());
                redisVo.setMerchantId(account.getId());
                redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
                redisService.putPaymentBill(redisVo);
                return ResultBean.successfulResult(result);
            }

            if (vo.getPaymentType() == PaymentTypeEnum.ALI.getValue()) {
                WechatAccount account = null;
                // 收款方式：0平台商户号收款 1企业商户号收款
                if (departmentHierarchy.getMerchantsType() == 0) {
                    // 0为平台收款商户号企业id
                    account = wechatAccountRepository.findByOrgIdAndType(0L,
                            PaymentTypeEnum.ALI.getValue());
                } else {
                    // 默认使用企业账户号收款
                    account = wechatAccountRepository.findByOrgIdAndType(orgId,
                            PaymentTypeEnum.ALI.getValue());
                }

                if (account == null) {
                    log.warn("BillAppService otherPayment 未找到支付宝配置,请联系负责人 orgId={}", orgId);
                    return ResultBean.failedResultWithMsg("未找到支付宝配置,请联系负责人");
                }
                // 其它费用类型转换
                String chargeTypeName = "";
                if (vo.getChargeType() == 1) {
                    chargeTypeName = "水费";
                } else if (vo.getChargeType() == 2) {
                    chargeTypeName = "电费";
                } else if (vo.getChargeType() == 3) {
                    chargeTypeName = "物业费";
                } else if (vo.getChargeType() == 4) {
                    chargeTypeName = "宽带费";
                } else if (vo.getChargeType() == 5) {
                    chargeTypeName = "电视费";
                } else if (vo.getChargeType() == 6) {
                    chargeTypeName = "燃气费";
                } else if (vo.getChargeType() == 7) {
                    chargeTypeName = "暖气费";
                } else if (vo.getChargeType() == 12) {
                    chargeTypeName = "其它费用组合支付";
                }
                String subject = "房屋管家-" + chargeTypeName;
                String passbackParams = outTradeNo;
                AliPay alipay = new AliPay(account.getMchId(), account.getPrivateKey(), account.getPublicKey(),
                        outTradeNo, totalAmount.toString(), subject, "QUICK_MSECURITY_PAY", passbackParams,
                        payConfig.getAliNotifyUrl());

                Map<String, Object> result = new AliPayRequest(alipay).appPay();

                if (result == null) {
                    return ResultBean.failedResultWithMsg("支付宝支付异常,请联系客服");
                }
                redisVo.setPublicKey(account.getPublicKey());
                redisVo.setMerchantId(account.getId());
                redisVo.setMerchantsType(departmentHierarchy.getMerchantsType());
                redisService.putPaymentBill(redisVo);
                return ResultBean.successfulResult(result);
            }
            return ResultBean.failedResultOfParamWithMsg("参数错误");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    public ResultBean<BillApiData<RentBillListVO>> backlogRentBillList(String accessToken, Long backlogItemId, Integer pageNumber, Integer pageSize) {
        ResultBean<BillApiData<RentBillListVO>> resultBean = new ResultBean<>();
        resultBean.setCode("10000");
        resultBean.setMsg("10000");
        BillApiData<RentBillListVO> apiData = new BillApiData<>();
        resultBean.setResultData(apiData);

        try {
            TokenRedisVo tokenRedisVo = redisService.findTokenVo(accessToken);
            if (tokenRedisVo == null) {
                return ResultBean.failedResultOfToken();
            }

            BacklogItemContractBill contractBill = backlogItemContractBillRepository.findByBacklogItemId(backlogItemId);
            if (contractBill == null) {
                return ResultBean.failedResultWithMsg("待办事项参数数据不存在");
            }
            List<Long> billIds = new ArrayList<>();
            billIds.add(contractBill.getRentBillId());
            billIds.add(contractBill.getDepositBillId());

            StringBuilder where = new StringBuilder();
            List<Object> args = new ArrayList<>();
            where.append(" where payment_status = 0 and id in " + SqlUtils.foreachIn(billIds.size()));
            args.addAll(billIds);

            // 总数
            Long total = jdbcTemplate.queryForObject("select count(1) from saas_rent_bill" + where, args.toArray(),
                    Long.class);
            if (total == 0) {
                return resultBean;
            }

            args.add(pageSize);
            args.add(pageNumber * pageSize);
            List<RentBill> data = jdbcTemplate.query("select * from saas_rent_bill" + where + SQL_PAGE,
                    new BeanPropertyRowMapper<>(RentBill.class), args.toArray());

            List<RentBillListVO> voList = new ArrayList<>();
            for (RentBill rentBill : data) {
                RentBillListVO vo = convert(rentBill);
                voList.add(vo);
            }

            // 查询小区名称
            List<String> residenceCommunityIds = voList.stream().filter(vo -> vo.getCommunityId() != null)
                    .map(RentBillListVO::getCommunityId).distinct().collect(Collectors.toList());
            if (!residenceCommunityIds.isEmpty()) {
                Map<String, String> map = communityRepository.findByIdIn(residenceCommunityIds).stream()
                        .collect(Collectors.toMap(Community::getId, Community::getName));
                if (!map.isEmpty()) {
                    voList.forEach(vo -> vo.setCommunityName(map.get(vo.getCommunityId())));
                }
            }
            // 查询楼栋名称
            List<Long> buildingIds = voList.stream().filter(vo -> vo.getBuildingNum() != null)
                    .map(RentBillListVO::getBuildingNum).distinct().collect(Collectors.toList());
            if (!buildingIds.isEmpty()) {
                Map<Long, String> map = communityBuildRepository.findByIdIn(buildingIds).stream()
                        .collect(Collectors.toMap(CommunityBuild::getId, CommunityBuild::getName));
                if (!map.isEmpty()) {
                    voList.forEach(vo -> vo.setBuildingName(map.get(vo.getBuildingNum())));
                }
            }
            // 查询单元名称
            List<Long> unitIds = voList.stream().filter(vo -> vo.getUnit() != null).map(RentBillListVO::getUnit)
                    .distinct().collect(Collectors.toList());
            if (!unitIds.isEmpty()) {
                Map<Long, String> map = communityBuildUnitRepository.findByIdIn(unitIds).stream()
                        .collect(Collectors.toMap(CommunityBuildUnit::getId, CommunityBuildUnit::getName));
                if (!map.isEmpty()) {
                    voList.forEach(vo -> vo.setUnitName(map.get(vo.getUnit())));
                }
            }
            // 查询承租人名称
            List<String> userIds = voList.stream().filter(vo -> vo.getMatchUserId() != null)
                    .map(RentBillListVO::getMatchUserId).distinct().collect(Collectors.toList());
            if (!userIds.isEmpty()) {
                List<AppUser> appUsers = appUserRepository.findByIdOrEnterpriseIn(userIds, userIds);
                for (AppUser appUser : appUsers) {
                    for (RentBillListVO vo : voList) {
                        if (vo.getMatchUserId().equals(appUser.getId()) || vo.getMatchUserId().equals(appUser.getEnterpriseUserId())) {
                            vo.setMatchUserName(appUser.getName());
                        }
                    }
                }
            }

            // 手续费比例
            String platStr = redisService.getValue("platform.commission");
            BigDecimal commission = DataUtils.IntegerToBigdecimal(Integer.valueOf(platStr));

            apiData.setData(voList);
            apiData.setTotal(total);
            apiData.setCommission(commission);
            resultBean.setResultData(apiData);
        } catch (Exception e) {
            log.error("BillAppService backlogRentBillList Exception = {}" + e.getMessage(), e);
            resultBean.setCode("10001");
            resultBean.setMsg("10001");
        }
        resultBean.setResultData(apiData);
        return resultBean;
    }

    public ResultBean<Map<String, Object>> zeroPayment(PaymentBillRedisVo redisVo) {
        try {
            ResultBean<Map<String, Object>> resultBean = new ResultBean<>();
            resultBean.setCode("10009");
            resultBean.setMsg("0元支付成功");

            List<RentBill> bills = rentBillRepository.findByIdIn(redisVo.getIds());
            if (CollectionUtils.isEmpty(bills)) {
                log.error("zeroPayment, 未找到相关费用信息 ids={}", redisVo.getIds());
                return ResultBean.failedResultWithMsg("未找到相关费用信息");
            }

            Date payTime = new Date();
            Integer billAmount = 0;

            List<HousePaymentOrderRelation> porList = new ArrayList<>();
            for (RentBill b : bills) {
                if (b.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    continue;
                }
                // 修改状态为已缴费
                b.setPaymentStatus(RrentBillPaymentStatusTypeEnum.PAY.getValue());
                b.setPaymentTime(payTime);
                b.setPaymentChannel(1);
                billAmount += b.getCost();

                HousePaymentOrderRelation por = new HousePaymentOrderRelation();
                por.setOrderNumber(redisVo.getOutTradeNo());
                por.setBillId(b.getId());
                por.setPlatformCommissionAmount(0);
                por.setBillAmount(b.getCost());
                if (b.getChargeType() == 1) {
                    por.setBusinessType(BusinessTypeEnum.RENT_FEE.getValue());
                } else if (b.getChargeType() == 2) {
                    por.setBusinessType(BusinessTypeEnum.DEPOSIT_FEE.getValue());
                }
                porList.add(por);
            }

            // 更新账单明细支付状态
            rentBillRepository.saveAll(bills);
            housePaymentOrderRelationRepository.saveAll(porList);

            /**
             * 验证租金、押金是否都支付完成，全部支付完成，才能修改公租房与合同状态
             */
            // 根据当前支付的账单明细，找到缴费账单对应的所有合同
            List<RentBillRelation> relationList = rentBillRelationRepository.findByRentBillIdIn(redisVo.getIds());
            Map<String, String> houseMap = new HashMap<>();
            List<String> contractNubmers = new ArrayList<>();
            for (RentBillRelation r : relationList) {
                houseMap.put(r.getContractNubmer(), r.getHouseId());
                contractNubmers.add(r.getContractNubmer());
            }
            contractNubmers = contractNubmers.stream().distinct().collect(Collectors.toList());
            // 查询拥有未支付的合同信息
            List<RentBillRelation> noPayBillList = rentBillRelationRepository.findNoPayBill(contractNubmers);

            // 保存全部支付完成(租金 + 押金)的合同编号
            List<String> completedContracts = new ArrayList<>();
            // 保存全部支付完成的公租房id
            List<String> houseIds = new ArrayList<>();

            // 验证合同是否支付完成
            Map<String, String> map = noPayBillList.stream().collect(Collectors.toMap(RentBillRelation::getContractNubmer, RentBillRelation::getHouseId));
            for (String contractNumber : contractNubmers) {
                String houseId = map.get(contractNumber);
                if (houseId == null) {
                    completedContracts.add(contractNumber);
                    String sHouseId = houseMap.get(contractNumber);
                    if (StringUtils.isNotBlank(sHouseId)) {
                        houseIds.add(sHouseId);
                    }
                }
            }

            // 更新公租房合同缴费状态
            if (!houseIds.isEmpty()) {
                List<House> houseList = houseRepository.findByIdIn(houseIds);
                houseList.forEach(h -> h.setPaymentStatus(HousePaymentStatusEnum.COMPLETE_PAY.getValue()));
                houseRepository.saveAll(houseList);
            }

            // 更新"已确认待交租"的合同状态
            if (!completedContracts.isEmpty()) {
                List<Contract> contractList = contractRepository.findByStatusAndContractNubmerIn(ContractStatusEnum.WAITING_RENT.getValue(), completedContracts);
                contractList.forEach(c -> c.setStatus(ContractStatusEnum.WAITING_RECORD.getValue()));
                contractRepository.saveAll(contractList);

                Date date = new Date();
                for (String contractNumber : completedContracts) {
                    //更新租金/押金待办事项状态
                    List<BacklogItemContractBill> contractBill = backlogItemContractBillRepository.findByContractNumber(contractNumber);
                    if (contractBill != null && contractBill.size()>0) {
                        for (BacklogItemContractBill bill : contractBill) {
                            Optional<BacklogItem> itemOpt = backlogItemRepository.findById(bill.getBacklogItemId());
                            if (itemOpt.isPresent()) {
                                BacklogItem item = itemOpt.get();
                                item.setBusinessStatus("已缴费");
                                item.setHandleStatus(1);
                                item.setHandlerTime(date);
                                backlogItemRepository.save(item);
                            }
                        }

                    }
                    //生成信息备案待提交待办事项
                    Contract contract = contractRepository.findByContractNubmer(contractNumber);
                    if (contract == null) {
                        log.error("zeroPayment 未找到合同信息, contractNumber={}", contractNumber);
                        return ResultBean.failedResultWithMsg("未找到合同信息");
                    }

                    AppUser appUsers = null;
                    if (StringUtils.isNotBlank(contract.getMatchUserId())){
                         appUsers = appUserRepository.findByIdOrEnterpriseUserId(contract.getMatchUserId(), contract.getMatchUserId());
                    }

                    BacklogItem submitItem = new BacklogItem();
                    submitItem.setHouseId(contract.getHouseId());
                    submitItem.setType(BacklogItemTypeEnum.RECORD_SUBMIT.getValue());
                    submitItem.setTitle("公租房信息备案待提交");
                    submitItem.setBusinessStatus("待提交");
                    submitItem.setHandleStatus(0);
                    submitItem.setMatchUserId(contract.getMatchUserId());
                    submitItem.setPhone(appUsers == null ? appUsers.getPhone() : null);
                    submitItem.setCreateTime(new Date());
                    BacklogItem save = backlogItemRepository.save(submitItem);

                    BacklogItemRecordSubmit submit = new BacklogItemRecordSubmit();
                    submit.setBacklogItemId(save.getId());
                    submit.setMatchUserId(contract.getMatchUserId());
                    backlogItemRecordSubmitRepository.save(submit);
                }
            }

            HousePaymentOrder paymentOrder = new HousePaymentOrder();
            paymentOrder.setBillAmount(billAmount);
            paymentOrder.setOrderNumber(redisVo.getOutTradeNo());
            paymentOrder.setPayType(1);
            paymentOrder.setPayMode(redisVo.getPaymentMode());
            paymentOrder.setPayTime(payTime);
            paymentOrder.setPlatformCommissionAmount(redisVo.getPlat());
            // 手续费 千分之6
            Integer payHandlingFee = new BigDecimal(billAmount * 0.006).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
            paymentOrder.setWxCommissionAmount(payHandlingFee);
            paymentOrder.setPayUserId(redisVo.getUserId());
            paymentOrder.setTotalAmount(0);
            paymentOrder.setPayResult(1);
            housePaymentOrderRepository.save(paymentOrder);

            return resultBean;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }

    public ResultBean<Map<String, Object>> zeroOtherPayment(PaymentBillRedisVo redisVo) {
        try {
            ResultBean<Map<String, Object>> resultBean = new ResultBean<>();
            resultBean.setCode("10009");
            resultBean.setMsg("0元支付成功");

            List<ExpenseBill> expenseBills = expenseBillRepository.findByIdIn(redisVo.getIds());
            if (CollectionUtils.isEmpty(expenseBills)) {
                log.error("zeroOtherPayment, 未找到相关费用信息 ids={}", redisVo.getIds());
                return ResultBean.failedResultWithMsg("未找到相关费用信息");
            }

            Date payTime = new Date();
            Integer billAmount = 0;

            List<HousePaymentOrderRelation> porList = new ArrayList<>();
            for (ExpenseBill b : expenseBills) {
                if (b.getPaymentStatus() == RrentBillPaymentStatusTypeEnum.PAY.getValue()) {
                    continue;
                }
                // 修改状态为已缴费
                b.setPaymentStatus(RrentBillPaymentStatusTypeEnum.PAY.getValue());
                b.setPaymentTime(payTime);
                b.setPaymentChannel(1);
                billAmount += b.getCost();

                HousePaymentOrderRelation por = new HousePaymentOrderRelation();
                por.setOrderNumber(redisVo.getOutTradeNo());
                por.setBillId(b.getId());
                por.setPlatformCommissionAmount(redisVo.getPlat());
                //por.setMerchantId(redisVo.getMerchantId());
                por.setBillAmount(b.getCost());
                // 其它费用类型转换
                if (b.getChargeType() == 1) {
                    por.setBusinessType(BusinessTypeEnum.WATER_FEE.getValue());
                } else if (b.getChargeType() == 2) {
                    por.setBusinessType(BusinessTypeEnum.ELECTRIC_FEE.getValue());
                } else if (b.getChargeType() == 3) {
                    por.setBusinessType(BusinessTypeEnum.PROPERTY_FEE.getValue());
                } else if (b.getChargeType() == 4) {
                    por.setBusinessType(BusinessTypeEnum.BROADBAND_FEE.getValue());
                } else if (b.getChargeType() == 5) {
                    por.setBusinessType(BusinessTypeEnum.TV_FEE.getValue());
                } else if (b.getChargeType() == 6) {
                    por.setBusinessType(BusinessTypeEnum.COALGAS_FEE.getValue());
                } else if (b.getChargeType() == 7) {
                    por.setBusinessType(BusinessTypeEnum.HEATING_FEE.getValue());
                } else if (b.getChargeType() == 10) {
                    // 退租
                    por.setBusinessType(BusinessTypeEnum.OTHER_FEE.getValue());
                    // 更新退租账单状态
                    withDrawService.changeStatus(b.getId(), b.getHouseId());
                }
                porList.add(por);
            }

            // 更新账单明细支付状态
            expenseBillRepository.saveAll(expenseBills);
            housePaymentOrderRelationRepository.saveAll(porList);

            HousePaymentOrder paymentOrder = new HousePaymentOrder();
            paymentOrder.setBillAmount(billAmount);
            paymentOrder.setOrderNumber(redisVo.getOutTradeNo());
            paymentOrder.setPayType(1);
            paymentOrder.setPayMode(redisVo.getPaymentMode());
            paymentOrder.setPayTime(payTime);
            paymentOrder.setPlatformCommissionAmount(redisVo.getPlat());
            // 手续费 千分之6
            Integer payHandlingFee = new BigDecimal(billAmount * 0.006).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
            paymentOrder.setWxCommissionAmount(payHandlingFee);
            paymentOrder.setPayUserId(redisVo.getUserId());
            paymentOrder.setTotalAmount(0);
            paymentOrder.setPayResult(1);
            housePaymentOrderRepository.save(paymentOrder);

            return resultBean;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return ResultBean.failedResultOfException();
        }
    }


}